

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/fluid.png">
  <link rel="icon" href="/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="深海火锅店">
  <meta name="keywords" content="">
  
    <meta name="description" content="Sentinel初识Sentinel雪崩问题 故障的服务D，会导致服务A的tomcat资源耗尽 微服务调用链路中的某个服务故障，引起整个链路中的所有微服务都不可用，这就是雪崩。   解决雪崩问题的常见方式有四种:  超时处理:设定超时时间，请求超过-定时间没有响应就返回错误信息，不会无休止等待 舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源，因此也叫线程隔离。 熔断降级:由断">
<meta property="og:type" content="article">
<meta property="og:title" content="Sentinel">
<meta property="og:url" content="http://example.com/2024/04/10/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/Java/%E5%AD%A6%E4%B9%A0/%E6%A1%86%E6%9E%B6/Sentinel/Sentinel/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="Sentinel初识Sentinel雪崩问题 故障的服务D，会导致服务A的tomcat资源耗尽 微服务调用链路中的某个服务故障，引起整个链路中的所有微服务都不可用，这就是雪崩。   解决雪崩问题的常见方式有四种:  超时处理:设定超时时间，请求超过-定时间没有响应就返回错误信息，不会无休止等待 舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源，因此也叫线程隔离。 熔断降级:由断">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403074558194.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403075332866.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403080118647.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403080917682.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403081023548.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407204039706.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407205129957.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407210436655.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407210503016.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407210759266.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211433491.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211555637.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211548557.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211931646.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407212903207.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213029296.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213116501.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213258792.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213305172.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408064325063.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065105430.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065329162.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065457513.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065517390.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065759385.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065827751.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070509839.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070703747.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070729937.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070746902.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070809963.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408071109096.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072009011.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072731930.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072739726.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072747154.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408073136305.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410075355925.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410075433734.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410075510088.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081054018.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081722803.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081732670.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081809138.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081841313.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081915014.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409073319242.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409073605422.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410082119553.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410082637264.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074149240.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074310444.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083103666.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083240454.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083454593.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083623355.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410084150507.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074723270.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074746502.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410210541628.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410210729986.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410210916311.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410211017304.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409075531220.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410220549522.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410220636398.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410220652041.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081053921.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081138373.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081246546.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410221758019.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410222328041.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081715561.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081844429.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214414971.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214733973.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214813076.png">
<meta property="og:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214914582.png">
<meta property="article:published_time" content="2024-04-10T00:00:00.000Z">
<meta property="article:modified_time" content="2024-04-10T14:25:33.513Z">
<meta property="article:author" content="深海火锅店">
<meta property="article:tag" content="Sentinel">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403074558194.png">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>Sentinel - Hexo</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"example.com","root":"/","version":"1.9.3","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>Fluid</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/img/background.jpg') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="Sentinel"></span>
          
        </div>

        
          
  <div class="mt-3">
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2024-04-10 08:00" pubdate>
          2024年4月10日 早上
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          21k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          175 分钟
        
      </span>
    

    
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">Sentinel</h1>
            
            
              <div class="markdown-body">
                
                <h1 id="Sentinel"><a href="#Sentinel" class="headerlink" title="Sentinel"></a>Sentinel</h1><h2 id="初识Sentinel"><a href="#初识Sentinel" class="headerlink" title="初识Sentinel"></a>初识Sentinel</h2><h3 id="雪崩问题"><a href="#雪崩问题" class="headerlink" title="雪崩问题"></a>雪崩问题</h3><blockquote>
<p>故障的服务D，会导致服务A的tomcat资源耗尽</p>
<p>微服务调用链路中的某个服务故障，引起整个链路中的所有微服务都不可用，这就是<code>雪崩</code>。</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403074558194.png" srcset="/img/loading.gif" lazyload alt="image-20240403074558194"></p>
<p><strong>解决雪崩问题的常见方式有四种:</strong></p>
<ol>
<li>超时处理:设定超时时间，请求超过-定时间没有响应就返回错误信息，不会无休止等待</li>
<li>舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat的资源，因此也叫线程隔离。</li>
<li>熔断降级:由断路器统计业务执行的异常比例，如果超出阈值则会熔断该业务，拦截访问该业务的一切请求。</li>
<li>流量控制:限制业务访问的QPS，避免服务因流量的突增而故障。</li>
</ol>
<h3 id="技术选型"><a href="#技术选型" class="headerlink" title="技术选型"></a>技术选型</h3><p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403075332866.png" srcset="/img/loading.gif" lazyload alt="image-20240403075332866"></p>
<blockquote>
<p>同类型的框架还有<code>Resilience4j</code>、<code>Polly</code>、<code>Envoy</code>等</p>
</blockquote>
<h3 id="安装Sentinel"><a href="#安装Sentinel" class="headerlink" title="安装Sentinel"></a>安装Sentinel</h3><p><a target="_blank" rel="noopener" href="https://github.com/alibaba/Sentinel/releases">Sentinel下载地址</a>，选择<code>sentinel-dashboard-xxx.jar</code>，自己新建一个<code>startup.txt</code></p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">java -jar sentinel-dashboard-1.8.5.jar<br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403080118647.png" srcset="/img/loading.gif" lazyload alt="image-20240403080118647"></p>
<blockquote>
<p>然后将<code>startup.txt</code>后缀改为<code>bat</code>，双击运行</p>
<p>或者自定义启动端口号</p>
</blockquote>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">java -Dserver.port=8718 -jar sentinel-dashboard-1.8.5.jar<br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403080917682.png" srcset="/img/loading.gif" lazyload alt="image-20240403080917682"></p>
<blockquote>
<p>账号密码默认是<code>sentinel</code></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240403081023548.png" srcset="/img/loading.gif" lazyload alt="image-20240403081023548"></p>
<p><strong>配置开机自启动</strong></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_52288743/article/details/131084881#:~:text=Win%20%2B%20R%20%E8%BE%93%E5%85%A5%E4%BB%A5%E4%B8%8B%E5%91%BD%E4%BB%A4%20%E5%9B%9E%E8%BD%A6%20shell%3ACommon%20Startup%20%E9%99%84%E5%8A%A0%EF%BC%9A%E5%A6%82%E9%9C%80%E6%9A%82%E5%81%9C%E8%87%AA%E5%90%AF%E5%8A%A8%E5%8F%AF%E4%BB%A5%E5%9C%A8%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8%E8%BF%9B%E8%A1%8C%E5%85%B3%E9%97%AD,%E5%9C%A8%20%E5%90%AF%E5%8A%A8%20%E5%90%8E%EF%BC%8C%E6%82%A8%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E8%AE%BF%E9%97%AE%20Sentinel%20%E7%9A%84%E6%8E%A7%E5%88%B6%E5%8F%B0%E6%9D%A5%E6%9F%A5%E7%9C%8B%E7%9B%91%E6%8E%A7%E6%95%B0%E6%8D%AE%E5%92%8C%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AF%E3%80%82%20%E9%BB%98%E8%AE%A4%E6%83%85%E5%86%B5%E4%B8%8B%EF%BC%8C%20Sentinel%20%E7%9A%84%E6%8E%A7%E5%88%B6%E5%8F%B0%E5%9C%B0%E5%9D%80%E4%B8%BA%EF%BC%9Ahttp%3A%2F%2Flocalhost%3A8080%E3%80%82">博客链接</a></p>
<h2 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h2><blockquote>
<p>在<code>order</code>服务导入<code>sentinel</code>依赖，并配置<code>application.properties</code><br>然后访问一下<code>order</code>服务的<code>controller</code> <a target="_blank" rel="noopener" href="http://localhost:8090/order/1">http://localhost:8090/order/1</a></p>
</blockquote>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-alibaba-sentinel<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-comment"># 配置控制台地址</span><br><span class="hljs-attr">spring.cloud.sentinel.transport.dashboard</span>=<span class="hljs-string">localhost:8089</span><br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407204039706.png" srcset="/img/loading.gif" lazyload alt="image-20240407204039706"></p>
<blockquote>
<p>sentinel主要采用<code>线程池隔离</code>、<code>熔断降级</code>、<code>流量控制</code>的方式来解决雪崩问题</p>
</blockquote>
<h2 id="簇点链路"><a href="#簇点链路" class="headerlink" title="簇点链路"></a>簇点链路</h2><p>簇点链路:就是项目内的调用链路,链路中被监控的每个接口就是一个资源。 默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint) ，因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一-个资源。</p>
<p>流控、熔断等都是针对簇点链路中的资源来设置的，因此我们可以点击对应资源后面的按钮来设置规则:</p>
<p><strong>需求</strong>:给&#x2F;order&#x2F;{orderld}这个资源设置流控规则，QPS不能超过5。然后进行压力测试。</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407205129957.png" srcset="/img/loading.gif" lazyload alt="image-20240407205129957"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407210436655.png" srcset="/img/loading.gif" lazyload alt="image-20240407210436655"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407210503016.png" srcset="/img/loading.gif" lazyload alt="image-20240407210503016"></p>
<h2 id="流控模式"><a href="#流控模式" class="headerlink" title="流控模式"></a>流控模式</h2><blockquote>
<p>在添加限流规则时，点击高级选项，可以选择三种流控模式:</p>
</blockquote>
<ol>
<li><code>直接</code>:统计当前资源的请求，触发阈值时对当前资源直接限流，也是默认的模式</li>
<li><code>关联</code>:统计与当前资源相关的另一一个资源，触发阈值时，对当前资源限流</li>
<li><code>链路</code>:统计从指定链路访问到本资源的请求，触发阈值时,对指定链路限流</li>
</ol>
<h3 id="关联模式"><a href="#关联模式" class="headerlink" title="关联模式"></a>关联模式</h3><p>统计与当前资源相关的另一个资源，触发阈值时，对当前资源限流</p>
<p>使用场景:比如用户支付时需要修改订单状态，同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时，需要对查询订单业务限流，防止影响到更新订单的业务。</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407210759266.png" srcset="/img/loading.gif" lazyload alt="image-20240407210759266"></p>
<p><strong>需求</strong></p>
<ol>
<li>在OrderController新建两个端点: <code>/order/query</code>和<code>/order/update</code>, 无需实现业务</li>
<li>配置流控规则，当<code>/order/update</code>资源被访问的QPS超过5时，对<code>/order/query</code>请求限流</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RefreshScope</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;order&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> OrdersService ordersService;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserFeignClient userFeignClient;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> Orders <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-type">Orders</span> <span class="hljs-variable">orders</span> <span class="hljs-operator">=</span> ordersService.getById(id);<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userFeignClient.findOne(orders.getUserId());<br>        orders.setUser(user);<br>        <span class="hljs-keyword">return</span> orders;<br>    &#125;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/query&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">query</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;查询订单成功&quot;</span>;<br>    &#125;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/update&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">update</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;更新订单成功&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>然后访问一下 <a target="_blank" rel="noopener" href="http://localhost:8090/order/query">http://localhost:8090/order/query</a> 和 <a target="_blank" rel="noopener" href="http://localhost:8090/order/update%E3%80%81">http://localhost:8090/order/update、</a></p>
<p>对<code>/order/query</code>添加流控</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211433491.png" srcset="/img/loading.gif" lazyload alt="image-20240407211433491"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211555637.png" srcset="/img/loading.gif" lazyload alt="image-20240407211555637"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211548557.png" srcset="/img/loading.gif" lazyload alt="image-20240407211548557"></p>
<blockquote>
<p>对<code>update</code>压力测试，限流<code>query</code></p>
<p>满足下面条件可以使用关联模式:</p>
<ol>
<li>两个有竞争关系的资源</li>
<li>一个<code>优先级较高</code>，一个<code>优先级较低</code>，我们希望当<code>优先级高</code>的<code>触发阈值</code>时，对<code>优先级低</code>的进行<code>限流</code></li>
</ol>
</blockquote>
<h3 id="链路模式"><a href="#链路模式" class="headerlink" title="链路模式"></a>链路模式</h3><p>链路模式:只针对从指定链路访问到本资源的请求做统计，判断是否超过阈值。<br>例如有两条请求链路:<br>&#x2F;test1 -&gt; &#x2F;common<br>&#x2F;test2 -&gt; &#x2F;common<br>如果只希望统计从&#x2F;test2进入到&#x2F;common的请求，则可以这样配置:</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407211931646.png" srcset="/img/loading.gif" lazyload alt="image-20240407211931646"></p>
<p><strong>需求</strong>:有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计并设置限流。<br>步骤:</p>
<ol>
<li>在OrderService 中添加一个queryGoods方法，不用实现业务</li>
<li>在OrderController中 ，改造&#x2F;order&#x2F;query端点，调用OrderService中的queryGoods方法</li>
<li>在OrderController中 添加一个&#x2F;order&#x2F;save的端 点,调用OrderService的queryGoods方法</li>
<li>给queryGoods设 置限流规则,从&#x2F;order&#x2F;query进 入queryGoods的方法限制QPS必须小于2</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ServiceImpl</span>&lt;OrdersMapper, Orders&gt;<br>    <span class="hljs-keyword">implements</span> <span class="hljs-title class_">OrdersService</span>&#123;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">queryGoods</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;查询商品成功&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RefreshScope</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;order&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> OrdersService ordersService;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserFeignClient userFeignClient;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> Orders <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-type">Orders</span> <span class="hljs-variable">orders</span> <span class="hljs-operator">=</span> ordersService.getById(id);<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userFeignClient.findOne(orders.getUserId());<br>        orders.setUser(user);<br>        <span class="hljs-keyword">return</span> orders;<br>    &#125;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/query&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">query</span><span class="hljs-params">()</span> &#123;<br>        ordersService.queryGoods();<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;查询订单成功&quot;</span>;<br>    &#125;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/update&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">update</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;更新订单成功&quot;</span>;<br>    &#125;<br><br>    <span class="hljs-meta">@GetMapping(&quot;/save&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">save</span><span class="hljs-params">()</span> &#123;<br>        ordersService.queryGoods();<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;创建订单成功&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>而<code>service</code>中的<code>queryGoods()</code>并没有被<code>sentinel</code>监控，无法配置限流规则，所以应该对其进行监控</p>
</blockquote>
<p>Sentinel默认只标记Controller中的方法为资源，如果要标记其它方法，需要利用<code>@SentinelResource</code>注解</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ServiceImpl</span>&lt;OrdersMapper, Orders&gt;<br>    <span class="hljs-keyword">implements</span> <span class="hljs-title class_">OrdersService</span>&#123;<br><br>    <span class="hljs-meta">@SentinelResource(&quot;goods&quot;)</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">queryGoods</span><span class="hljs-params">()</span> &#123;<br>        System.out.println(<span class="hljs-string">&quot;查询商品成功&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效</p>
<p>当 Sentinel 默认开启 <code>web-context-unify</code> 时，会将所有 Controller 方法的上下文路径进行整合，这意味着所有通过 Controller 的调用都会被视作一个统一的资源路径，这可能会导致链路模式下的流控规则失效。</p>
<p>具体来说，假设你有一个服务内部有多个接口相互调用形成了一条调用链路，如 <code>/save -&gt; /goods</code> 和 <code>/query -&gt; /goods</code>，如果你想要针对 <code>/query </code> 调用到 <code>/goods</code> 的这条特定调用链路设置流量控制，那么在 context 整合的情况下，Sentinel 无法区分这两个不同的调用链路，因为它们在上下文路径上都被视为同一个资源。</p>
<p>需要修改<code>application.properties</code>,添加配置</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-comment"># 配置控制台地址</span><br><span class="hljs-attr">spring.cloud.sentinel.transport.dashboard</span>=<span class="hljs-string">localhost:8089</span><br><span class="hljs-comment"># 关闭 context 整合</span><br><span class="hljs-attr">spring.cloud.sentinel.web-context-unify</span>=<span class="hljs-string">false</span><br></code></pre></td></tr></table></figure>

<blockquote>
<p>如果不关闭<code>context整合</code>，所有的<code>controller</code>会被认为是同一个根链路发展而来的子链路</p>
<p>访问一下 <a target="_blank" rel="noopener" href="http://localhost:8090/order/query">http://localhost:8090/order/query</a> 和 <a target="_blank" rel="noopener" href="http://localhost:8090/order/save">http://localhost:8090/order/save</a></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407212903207.png" srcset="/img/loading.gif" lazyload alt="image-20240407212903207"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213029296.png" srcset="/img/loading.gif" lazyload alt="image-20240407213029296"></p>
<blockquote>
<p>删除其他的流控规则</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213116501.png" srcset="/img/loading.gif" lazyload alt="image-20240407213116501"></p>
<blockquote>
<p>使用<code>Jmeter</code>进行压力测试</p>
</blockquote>
<p><strong>&#x2F;order&#x2F;save</strong></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213258792.png" srcset="/img/loading.gif" lazyload alt="image-20240407213258792"></p>
<p><strong>&#x2F;order&#x2F;query</strong></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240407213305172.png" srcset="/img/loading.gif" lazyload alt="image-20240407213305172"></p>
<p><strong>总结</strong></p>
<p>直接:对当前资源限流<br>关联:高优先级资源触发阈值，对低优先级资源限流。<br>链路:阈值统计时，只统计从指定资源进入当前资源的请求是对请求来源的限流</p>
<h2 id="流控效果"><a href="#流控效果" class="headerlink" title="流控效果"></a>流控效果</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>流控效果是指请求达到流控阈值时应该采取的措施，包括三种:</p>
<ol>
<li><code>快速失败</code>:达到阈值后，新的请求会被立即拒绝并拋出FlowException异常。是默认的处理方式。</li>
<li><code>warm up</code>:预热模式，对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化，从一-个较小值逐渐增加到最大阈值。</li>
<li><code>排队等待</code>:让所有的请求按照先后次序排队执行，两个请求的间隔不能小于指定时长</li>
</ol>
<h3 id="预热模式warm-up"><a href="#预热模式warm-up" class="headerlink" title="预热模式warm up"></a>预热模式warm up</h3><p>warm up也叫预热模式，是应对服务冷启动的一种方案。<code>threshold</code>为最大阈值，<code>coldFactor</code>为冷启动因子，请求阈值初始值是<code>threshold/coldFactor</code>,持续指定时长后，逐渐提高到<code>threshold</code>值。而<code>coldFactor</code>的默认值是3.</p>
<p>例如，我设置QPS的<code>threshold</code>为10，预热时间为5秒,那么<code>初始阈值</code>就是<code>10/3</code>，也就是3,然后在5秒后逐渐增长到10</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408064325063.png" srcset="/img/loading.gif" lazyload alt="image-20240408064325063"></p>
<p><strong>需求</strong></p>
<p>给<code>/order/&#123;id&#125;</code>这个资源设置限流,最大QPS为10,利用warm up效果,预热时长为5秒</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065105430.png" srcset="/img/loading.gif" lazyload alt="image-20240408065105430"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065329162.png" srcset="/img/loading.gif" lazyload alt="image-20240408065329162"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065457513.png" srcset="/img/loading.gif" lazyload alt="image-20240408065457513"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065517390.png" srcset="/img/loading.gif" lazyload alt="image-20240408065517390"></p>
<h3 id="排队等待"><a href="#排队等待" class="headerlink" title="排队等待"></a>排队等待</h3><p>当请求超过QPS阈值时，快速失败和warm up会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成，如果请求预期的等待时间超出最大时长,则会被拒绝。<br>例如: QPS&#x3D;5,意味着每<code>200ms</code>处理一个队列中的请求; timeout &#x3D; 2000， 意味着预期等待超过2000ms的请求会被拒绝并抛出异常</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065759385.png" srcset="/img/loading.gif" lazyload alt="image-20240408065759385"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408065827751.png" srcset="/img/loading.gif" lazyload alt="image-20240408065827751"></p>
<p><strong>需求</strong></p>
<p>给<code>/order/&#123;id&#125;</code>这个资源设置限流，最大QPS为10， 利用排队的流控效果，超时时长设置为5s</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070509839.png" srcset="/img/loading.gif" lazyload alt="image-20240408070509839"></p>
<blockquote>
<p>而<code>Jmeter</code>配置的<code>QPS</code>是15，如果按照之前的情况（直接失败），则会有<code>5</code>个<code>QPS</code>无法处理</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070703747.png" srcset="/img/loading.gif" lazyload alt="image-20240408070703747"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070729937.png" srcset="/img/loading.gif" lazyload alt="image-20240408070729937"></p>
<blockquote>
<p>到后面请求超时则会直接失败</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070746902.png" srcset="/img/loading.gif" lazyload alt="image-20240408070746902"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408070809963.png" srcset="/img/loading.gif" lazyload alt="image-20240408070809963">	</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>流控效果有哪些？</p>
<ol>
<li>快速失败: QPS超过阈值时，拒绝新的请求</li>
<li>warm up: QPS超过阈值时，拒绝新的请求; QPS阈值是逐渐提升的，可以避免冷启动时高并发导致服务宕机。</li>
<li>排队等待:请求会进入队列，按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间，直接拒绝</li>
</ol>
<h2 id="热点参数限流"><a href="#热点参数限流" class="headerlink" title="热点参数限流"></a>热点参数限流</h2><blockquote>
<p>之前的限流是统计访问某个资源的所有请求，判断是否超过QPS阈值。而热点参数限流是分别统计<code>参数值相同</code>的请求,判断是否超过QPS阈值。</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408071109096.png" srcset="/img/loading.gif" lazyload alt="image-20240408071109096"></p>
<blockquote>
<p><strong>案例</strong></p>
<p>给<code>/order/&#123;id&#125;</code>这个资源添加热点参数限流，规则如下:</p>
<ol>
<li>默认的热点参数规则是每1秒请求量不超过<code>2</code></li>
<li>给1这个参数设置例外:每1秒请求量不超过<code>4</code></li>
<li>给2这个参数设置例外:每1秒请求量不超过<code>10</code></li>
</ol>
<p>需要<code>注意</code>的是<code>热点参数限流</code>对默认的SpringMVC资源无效，只有通过<code>@SentinelResource</code>注解声明的资源才能生效</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RefreshScope</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;order&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> OrdersService ordersService;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserFeignClient userFeignClient;<br><br>    <span class="hljs-meta">@SentinelResource(&quot;hot&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> Orders <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-type">Orders</span> <span class="hljs-variable">orders</span> <span class="hljs-operator">=</span> ordersService.getById(id);<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userFeignClient.findOne(orders.getUserId());<br>        orders.setUser(user);<br>        <span class="hljs-keyword">return</span> orders;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072009011.png" srcset="/img/loading.gif" lazyload alt="image-20240408072009011"></p>
<blockquote>
<p>可以看到<code>/order/1</code>的<code>QPS</code>是4</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072731930.png" srcset="/img/loading.gif" lazyload alt="image-20240408072731930"></p>
<blockquote>
<p>可以看到<code>/order/2</code>的<code>QPS</code>是10，全部通过</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072739726.png" srcset="/img/loading.gif" lazyload alt="image-20240408072739726"></p>
<blockquote>
<p>可以看到<code>/order/3</code>的<code>QPS</code>是默认阈值，即<code>2</code></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408072747154.png" srcset="/img/loading.gif" lazyload alt="image-20240408072747154"></p>
<h2 id="隔离和降级"><a href="#隔离和降级" class="headerlink" title="隔离和降级"></a>隔离和降级</h2><blockquote>
<p><strong>虽然限流可以尽量避免因高并发而引起的服务故障，但服务还会因为其它原因而故障。而要将这些故障控制在一定范围，避免雪崩，就要靠线程隔离(舱壁模式)和熔断降级手段了。</strong></p>
<p>不管是线程隔离还是熔断降级,都是对<code>客户端(调用方，示例中的服务A)</code>的保护。</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240408073136305.png" srcset="/img/loading.gif" lazyload alt="image-20240408073136305"></p>
<p>SpringCloud中，微服务调用都是通过Feign来实现的，因此做客户端保护必须整合Feign和Sentinel。</p>
<ol>
<li>修改OrderService的<code>application.properties</code>文件，开启Feign的Sentinel功能</li>
</ol>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">feign.sentinel.enabled</span>=<span class="hljs-string">true</span><br></code></pre></td></tr></table></figure>

<ol start="2">
<li><p>给FeignClient编写失败后的降级逻辑，但调用的服务异常时，返回<code>备用</code>方案</p>
<ul>
<li><p>方式一: FallbackClass,无法对远程调用的异常做处理</p>
</li>
<li><p>方式二: FallbackFactory,可以对远程调用的异常做处理，我们选择这种</p>
</li>
</ul>
</li>
<li><p>在<code>feign-api</code>项目中定义类，实现<code>FallbackFactory</code></p>
</li>
</ol>
<blockquote>
<p>不少教程导入的是<code>hystrix</code>包下的<code>FallbackFactory</code>，但是<code>sentinel</code>已经不再使用<code>hystrix</code>依赖了，而<code>openfeign</code>包下也有一个<code>FallbackFactory</code></p>
<p>不确定是不是这个原因导致后文报错</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserClientFallbackFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">FallbackFactory</span>&lt;UserFeignClient&gt; &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> UserFeignClient <span class="hljs-title function_">create</span><span class="hljs-params">(Throwable cause)</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UserFeignClient</span>() &#123;<br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-keyword">public</span> User <span class="hljs-title function_">findOne</span><span class="hljs-params">(Integer id)</span> &#123;<br>                log.info(<span class="hljs-string">&quot;findOne方法异常，执行降级逻辑&quot;</span>);<br>                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br>            &#125;<br>        &#125;;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ol start="2">
<li>在<code>feign-api</code>项目中将<code>UserClientFallbackFactory</code>注册为一个Bean:</li>
</ol>
<p>这里没有加<code>@Configuration</code>注解是因为在<code>OrderApplication</code>配置了<code>@EnableFeignClients</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FeignClientConfiguration</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> Logger.Level <span class="hljs-title function_">feignLogLevel</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> Logger.Level.BASIC;<br>    &#125;<br><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> UserClientFallbackFactory <span class="hljs-title function_">userClientFallbackFactory</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UserClientFallbackFactory</span>();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class,</span><br><span class="hljs-meta">        basePackageClasses = &#123;</span><br><span class="hljs-meta">                UserFeignClient.class</span><br><span class="hljs-meta">        &#125;)</span><br><span class="hljs-meta">@EnableDiscoveryClient</span><br><span class="hljs-meta">@MapperScan(&quot;com.example.order.mapper&quot;)</span><br><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderApplication</span> &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        SpringApplication.run(OrderApplication.class, args);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<ol start="3">
<li>在<code>feign-api</code>项目中的<code>UserClient</code>接口中使用<code>UserClientFallbackFactory</code>:</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@FeignClient(value = &quot;user-service&quot;,fallbackFactory = UserClientFallbackFactory.class)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserFeignClient</span> &#123;<br>    <span class="hljs-meta">@GetMapping(&quot;/user/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> User <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable(value = &quot;id&quot;)</span> Integer id)</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java">org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name <span class="hljs-string">&#x27;consumerController&#x27;</span>: Unsatisfied dependency expressed through field <span class="hljs-string">&#x27;providerClient&#x27;</span>: Error creating bean with name <span class="hljs-string">&#x27;feignSentinelBuilder&#x27;</span> defined in <span class="hljs-keyword">class</span> <span class="hljs-title class_">path</span> resource [com/alibaba/cloud/sentinel/feign/SentinelFeignAutoConfiguration.class]: Post-processing of merged bean definition failed<br><br>Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name <span class="hljs-string">&#x27;feignSentinelBuilder&#x27;</span> defined in <span class="hljs-keyword">class</span> <span class="hljs-title class_">path</span> resource [com/alibaba/cloud/sentinel/feign/SentinelFeignAutoConfiguration.class]: Post-processing of merged bean definition failed<br><br>Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.alibaba.cloud.sentinel.feign.SentinelFeign$Builder] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b]<br><br>Caused by: java.lang.NoClassDefFoundError: org/springframework/cloud/openfeign/FeignClientFactory<br><br>Caused by: java.lang.ClassNotFoundException: org.springframework.cloud.openfeign.FeignClientFactory<br>	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:<span class="hljs-number">641</span>) ~[na:na]<br>	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:<span class="hljs-number">188</span>) ~[na:na]    <br></code></pre></td></tr></table></figure>

<blockquote>
<p>这里整合feign各种报错，估计是版本原因</p>
<p>我尝试使用<code>2.3.12.RELEASE</code>的springboot是可以实现<code>feign + nacos + sentinel</code>的(还是jdk17)</p>
<p>demo链接放到github上了，<a target="_blank" rel="noopener" href="https://github.com/IsUnderAchiever/sentinel-demo">链接</a></p>
<p>做了个<code>springboot3、nacos、openfeign、sentinel</code>的demo，还是上面的这个链接，请切换<code>boot3</code>版本</p>
<p>springboot3报错解决，不使用<code>UserClientFallbackFactory</code>，删除该文件，<code>feign.sentinel.enabled</code>配置也删除</p>
<p>使用<code>@SentinelResource</code>配合<code>blockHandler</code>属性</p>
</blockquote>
<p><strong>在服务被调用方新增</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;provider&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ProviderController</span> &#123;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;providerQuery&quot;,blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/query&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">query</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;query...&quot;</span>;<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">blockHandler</span><span class="hljs-params">(BlockException e)</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;请稍后再试！&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><strong>添加流控规则</strong></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410075355925.png" srcset="/img/loading.gif" lazyload alt="image-20240410075355925"></p>
<p><strong>访问provider服务</strong></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410075433734.png" srcset="/img/loading.gif" lazyload alt="image-20240410075433734"></p>
<blockquote>
<p>访问<code>provider</code>自然是没有问题的，关键是<code>consumer</code>那边怎么样</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410075510088.png" srcset="/img/loading.gif" lazyload alt="image-20240410075510088"></p>
<blockquote>
<p>可以，也被限流了</p>
<p>需要注意的是<code>blockHandler</code>指定的方法返回值要和<code>被标记@SentinelResource</code>的方法<code>(这里是query)</code>保持一致，得是<code>String</code></p>
<p>否则会报错如下</p>
</blockquote>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs stylus">com<span class="hljs-selector-class">.alibaba</span><span class="hljs-selector-class">.csp</span><span class="hljs-selector-class">.sentinel</span><span class="hljs-selector-class">.slots</span><span class="hljs-selector-class">.block</span><span class="hljs-selector-class">.flow</span><span class="hljs-selector-class">.FlowException</span>: null<br></code></pre></td></tr></table></figure>

<blockquote>
<p><strong>我们希望能同意来配置</strong>，而不是每个类都写一个单独的限流方法</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 全局限流处理</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span>: 不是菜狗爱编程</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@date</span>: 2024/04/10/7:27</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@description</span>:</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobeBlockException</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">blockHandler</span><span class="hljs-params">(BlockException e)</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;您访问太频繁了，请稍后再试！&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;provider&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ProviderController</span> &#123;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;providerQuery&quot;,blockHandlerClass = GlobeBlockException.class, blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/query&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">query</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;query...&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>以上代码在github中，<a target="_blank" rel="noopener" href="https://github.com/IsUnderAchiever/sentinel-demo">链接</a></p>
<p><strong>注意切换boot3分支，main分支使用的是2.3.12.RELEASE版本springboot，<code>2.3.12.RELEASE版本springboot</code>可以正常使用自定义<code>FallbackFactory</code>类</strong></p>
<p>以上是关于大概的解法，下面展示<code>order</code>和<code>user</code>模块的写法</p>
</blockquote>
<p><strong>user模块</strong></p>
<blockquote>
<p>注意这里设置的<code>blockHandler</code>方法参数要比<code>@SentinelResource</code>标记的方法参数多一个<code>BlockException</code>，其它参数保持一致</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobeBlockException</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> User <span class="hljs-title function_">blockHandler</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id,BlockException e)</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;user&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserService userService;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;userQueryById&quot;,blockHandlerClass = GlobeBlockException.class,blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> User <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-keyword">return</span> userService.getById(id);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081054018.png" srcset="/img/loading.gif" lazyload alt="image-20240410081054018"></p>
<blockquote>
<p>又出现一个问题，springboot启动多个user实例，但是sentinel设置限流规则后只对其中一个实例有效</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081722803.png" srcset="/img/loading.gif" lazyload alt="image-20240410081722803"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081732670.png" srcset="/img/loading.gif" lazyload alt="image-20240410081732670"></p>
<blockquote>
<p>看<code>sentinel</code>界面</p>
<p>在这里设置多个即可</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081809138.png" srcset="/img/loading.gif" lazyload alt="image-20240410081809138"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081841313.png" srcset="/img/loading.gif" lazyload alt="image-20240410081841313"></p>
<blockquote>
<p>order也有效</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410081915014.png" srcset="/img/loading.gif" lazyload alt="image-20240410081915014"></p>
<h2 id="线程隔离"><a href="#线程隔离" class="headerlink" title="线程隔离"></a>线程隔离</h2><blockquote>
<p>左侧是<code>线程隔离</code>，右侧是<code>信号量隔离</code></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409073319242.png" srcset="/img/loading.gif" lazyload alt="image-20240409073319242"></p>
<blockquote>
<p>对比</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409073605422.png" srcset="/img/loading.gif" lazyload alt="image-20240409073605422"></p>
<blockquote>
<p>将原本的<code>QPS</code>换成<code>线程数</code>即可</p>
</blockquote>
<p><strong>需求</strong></p>
<p>给UserClient的查询用户接口设置流控规则，线程数不能超过2。然后利用jemeter测试。</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410082119553.png" srcset="/img/loading.gif" lazyload alt="image-20240410082119553"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410082637264.png" srcset="/img/loading.gif" lazyload alt="image-20240410082637264"></p>
<blockquote>
<p>这里的请求都是正常，因为之前配置了降级的逻辑，所以会直接返回一个<code>空的user对象</code>，而不是报错</p>
</blockquote>
<h2 id="熔断降级"><a href="#熔断降级" class="headerlink" title="熔断降级"></a>熔断降级</h2><h3 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h3><p>熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例，如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074149240.png" srcset="/img/loading.gif" lazyload alt="image-20240409074149240"></p>
<h3 id="熔断策略–慢调用"><a href="#熔断策略–慢调用" class="headerlink" title="熔断策略–慢调用"></a>熔断策略–慢调用</h3><p>断路器熔断策略有三种:<code>慢调用</code>、<code>异常比例</code>、<code>异常数</code><br>慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内，如果请求数量超过设定的最小数量，慢调用比例大于设定的阈值，则触发熔断。例如:</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074310444.png" srcset="/img/loading.gif" lazyload alt="image-20240409074310444"></p>
<p><strong>解读</strong>: RT超过500ms的调用是慢调用，统计最近10000ms内的请求，如果请求量超过10次,并且慢调用比例不低于0.5则触发熔断，熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。</p>
<p><strong>需求</strong></p>
<blockquote>
<p>给UserClient的查询用户接口设置降级规则，慢调用的RT阈值为50ms,统计时间为1秒，最小请求数量为5，失败阈值比例为0.4,熔断时长为5</p>
<p>为了增加业务耗时，可以设置<code>Thread.sleep(60)</code></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083103666.png" srcset="/img/loading.gif" lazyload alt="image-20240410083103666"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;user&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserService userService;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;userQueryById&quot;,blockHandlerClass = GlobeBlockException.class,blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> User <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-keyword">try</span> &#123;<br>            Thread.sleep(<span class="hljs-number">60</span>);<br>        &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br>        &#125;<br>        <span class="hljs-keyword">return</span> userService.getById(id);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>此时我们正常的响应时间是<code>71ms</code></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083240454.png" srcset="/img/loading.gif" lazyload alt="image-20240410083240454"></p>
<blockquote>
<p>快速访问多次后熔断，熔断之后已经不再去尝试了，而是直接返回失败结果，这里的时间为<code>4ms</code></p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083454593.png" srcset="/img/loading.gif" lazyload alt="image-20240410083454593"></p>
<blockquote>
<p>服务的调用方响应如下</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410083623355.png" srcset="/img/loading.gif" lazyload alt="image-20240410083623355"></p>
<blockquote>
<p>针对多实例也有效</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410084150507.png" srcset="/img/loading.gif" lazyload alt="image-20240410084150507"></p>
<h3 id="熔断策略–异常比例、异常数"><a href="#熔断策略–异常比例、异常数" class="headerlink" title="熔断策略–异常比例、异常数"></a>熔断策略–异常比例、异常数</h3><p>异常比例或异常数:统计指定时间内的调用，如果调用次数超过指定请求数，并且出现异常的比例达到设定的比例阈值(或超过指定异常数) ,则触发熔断。例如:</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074723270.png" srcset="/img/loading.gif" lazyload alt="image-20240409074723270"></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409074746502.png" srcset="/img/loading.gif" lazyload alt="image-20240409074746502"></p>
<p><strong>解读</strong>:统计最近1000ms内的请求，如果请求量超过10次，并且异常比例不低于0.5,则触发熔断，熔断时长为5秒。然后进入half-open状态，放行一次 请求做测试。</p>
<blockquote>
<p>故意抛出异常即可</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410210541628.png" srcset="/img/loading.gif" lazyload alt="image-20240410210541628"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;user&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserService userService;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;userQueryById&quot;,blockHandlerClass = GlobeBlockException.class,blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> User <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-keyword">if</span>(id==<span class="hljs-number">2</span>)&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;抛出异常了&quot;</span>);<br>        &#125;<br>        <span class="hljs-keyword">return</span> userService.getById(id);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410210729986.png" srcset="/img/loading.gif" lazyload alt="image-20240410210729986"></p>
<blockquote>
<p>如果觉得直接返回报错不太友好，可以使用<code>@RestControllerAdvice</code>来进行统一的异常处理，这里不过多介绍这个注解，先看看熔断是否生效</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410210916311.png" srcset="/img/loading.gif" lazyload></p>
<blockquote>
<p>再看看<code>order</code>服务调用是否正常</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410211017304.png" srcset="/img/loading.gif" lazyload alt="image-20240410211017304"></p>
<h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><p>Sentinel熔断降级的策略有哪些?</p>
<p>慢调用比例:超过指定时长的调用为慢调用，统计单位时长内慢调用的比例，超过阈值则熔断<br>异常比例:统计单位时长内异常调用的比例，超过阈值则熔断<br>异常数:统计单位时长内异常调用的次数,超过阈值则熔断</p>
<h2 id="授权规则"><a href="#授权规则" class="headerlink" title="授权规则"></a>授权规则</h2><blockquote>
<p>当时在<code>gateway</code>网关的时候有做过<code>过滤</code>，可以阻止某些请求通过<code>网关</code>路由到<code>微服务</code></p>
<p>但如果有一天<code>微服务</code>的地址及端口被泄露出去了，请求就可能不通过网关，直接访问服务的的地址，这个时候<code>微服务</code>就很危险了</p>
</blockquote>
<p>授权规则可以对调用方的来源做控制，有白名单和黑名单两种方式。<br>白名单:来源(origin) 在白名单内的调用者允许访问<br>黑名单:来源(origin)在黑名单内的调用者不允许访问</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409075531220.png" srcset="/img/loading.gif" lazyload alt="image-20240409075531220"></p>
<p>例如，我们限定只允许从网关来的请求访问<code>order-service</code>，不允许浏览器来直接访问,那么流控应用中就填写网关的名称.</p>
<p>这里的<code>流控应用</code>其实不是指的<code>网关服务</code>的名称，而是指的<code>orign</code>请求来源的名称</p>
<p>Sentinel是通过<code>RequestOriginParser</code>这个接口的parseOrigin来获取请求的来源的。</p>
<p>而<code>sentinel</code>默认情况下，从<code>gateway</code>和<code>浏览器</code>到达的<code>orign</code>名称都是<code>default</code>，无法对<code>网关</code>和<code>浏览器进行区分</code></p>
<p>所以需要实现<code>RequestOriginParser</code>该接口来自定义实现，比如</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HeaderOriginParser</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">RequestOriginParser</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">parse0rigin</span><span class="hljs-params">(HttpServletRequest request)</span> &#123;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">origin</span> <span class="hljs-operator">=</span> request. getHeader(<span class="hljs-string">&quot;origin&quot;</span>) ;<br>        <span class="hljs-keyword">if</span>(Str ingUtils. isEmpty(origin))&#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;blank&quot;</span>;<br>        &#125;<br>        <span class="hljs-keyword">return</span> origin;<br>	&#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>但现实是<code>网关</code>和<code>浏览器</code>默认都没有<code>origin</code>请求头，而我们给<code>网关</code>来的请求添加上<code>origin</code>即可</p>
<p>网关过滤器<code>AddRequestHeader</code>可以实现，这里直接写成<code>默认过滤器</code>，所有经过<code>网关</code>的请求都添加上<code>origin</code>头</p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>    <span class="hljs-attr">cloud:</span><br>        <span class="hljs-attr">gateway:</span><br>            <span class="hljs-attr">default-filters:</span><br>                <span class="hljs-comment">#添加名为origin的请求头，值为gateway</span><br>                <span class="hljs-string">-AddRequestHeader=origin,gateway</span> <br></code></pre></td></tr></table></figure>

<blockquote>
<p>所以<code>流控应用</code>就填<code>gateway</code>，注意这里的值，即<code>gateway</code>不要暴露了</p>
</blockquote>
<p><strong>理论结束，实践开始</strong></p>
<blockquote>
<p>order服务</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HeadOriginParser</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">RequestOriginParser</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">parseOrigin</span><span class="hljs-params">(HttpServletRequest httpServletRequest)</span> &#123;<br>        <span class="hljs-comment">// 获取请求头</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">origin</span> <span class="hljs-operator">=</span> httpServletRequest.getHeader(<span class="hljs-string">&quot;origin&quot;</span>);<br>        <span class="hljs-comment">// 判空</span><br>        <span class="hljs-keyword">if</span>(!StringUtils.hasLength(origin))&#123;<br>            <span class="hljs-comment">// origin为空</span><br>            origin=<span class="hljs-string">&quot;blank&quot;</span>;<br>        &#125;<br>        log.info(<span class="hljs-string">&quot;当前请求:&#123;&#125;,origin请求头:&#123;&#125;&quot;</span>,httpServletRequest.getRequestURI(),origin);<br>        <span class="hljs-keyword">return</span> origin;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>网关服务配置，主要是<code>- AddRequestHeader=origin,gateway</code></p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">gateway-service</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">nacos:</span><br>      <span class="hljs-attr">config:</span><br>        <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br>        <span class="hljs-attr">namespace:</span> <span class="hljs-string">d2455f6d-ed00-41ba-9915-09021bc0df6c</span><br>        <span class="hljs-attr">import-check:</span><br>          <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span><br>      <span class="hljs-attr">discovery:</span><br>        <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br>        <span class="hljs-attr">namespace:</span> <span class="hljs-string">d2455f6d-ed00-41ba-9915-09021bc0df6c</span><br>    <span class="hljs-attr">gateway:</span><br>      <span class="hljs-comment"># 网关路由配置</span><br>      <span class="hljs-attr">routes:</span><br>        <span class="hljs-comment"># 路由id，自定义唯一即可</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">user-service</span><br>          <span class="hljs-comment"># 目标路由地址，这种方式用的较少</span><br>          <span class="hljs-comment"># uri: http://localhost:8080</span><br>          <span class="hljs-comment"># lb就是LoadBalancer，负载均衡的意思，后面是服务名称</span><br>          <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://user-service</span><br>          <span class="hljs-comment"># 路由断言，判断请求是否符合路由规则条件</span><br>          <span class="hljs-attr">predicates:</span><br>            <span class="hljs-comment"># 路径匹配。匹配以/user/开头的路由</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/user/**</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">order-service</span><br>          <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://order-service</span><br>          <span class="hljs-attr">predicates:</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/order/**</span><br>      <span class="hljs-attr">default-filters:</span><br><span class="hljs-comment">#        - AddRequestHeader=color,this is red</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">AddRequestHeader=origin,gateway</span><br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410220549522.png" srcset="/img/loading.gif" lazyload alt="image-20240410220549522"></p>
<blockquote>
<p>通过网关服务访问</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410220636398.png" srcset="/img/loading.gif" lazyload alt="image-20240410220636398"></p>
<blockquote>
<p>直接访问</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410220652041.png" srcset="/img/loading.gif" lazyload alt="image-20240410220652041"></p>
<h2 id="自定义异常结果"><a href="#自定义异常结果" class="headerlink" title="自定义异常结果"></a>自定义异常结果</h2><p>默认情况下，发生限流、降级、授权拦截时，都会抛出异常到调用方。如果要自定义异常时的返回结果，需要实现<code>BlockExceptionHandler</code>接口</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081053921.png" srcset="/img/loading.gif" lazyload alt="image-20240409081053921"></p>
<blockquote>
<p>判断这里异常的类型，然后返回自定义结果</p>
</blockquote>
<p><strong>而<code>BlockException</code>包含很多个子类，分别对应不同的场景:</strong></p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081138373.png" srcset="/img/loading.gif" lazyload alt="image-20240409081138373"></p>
<blockquote>
<p>具体写法可参考</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081246546.png" srcset="/img/loading.gif" lazyload alt="image-20240409081246546"></p>
<blockquote>
<p>order服务</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SentinelBlockHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">BlockExceptionHandler</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">handle</span><span class="hljs-params">(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">msg</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;未知异常&quot;</span>;<br>        <span class="hljs-type">int</span> <span class="hljs-variable">status</span> <span class="hljs-operator">=</span> <span class="hljs-number">429</span>;<br>        <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> FlowException) &#123;<br>            msg = <span class="hljs-string">&quot;请求被限流了! &quot;</span>;<br>        &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> DegradeException) &#123;<br>            msg = <span class="hljs-string">&quot;请求被降级了! &quot;</span>;<br>        &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> ParamFlowException) &#123;<br>            msg = <span class="hljs-string">&quot;热点参数限流! &quot;</span>;<br>        &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (e <span class="hljs-keyword">instanceof</span> AuthorityException) &#123;<br>            msg = <span class="hljs-string">&quot;请求没有权限! &quot;</span>;<br>            status = <span class="hljs-number">401</span>;<br>        &#125;<br>        httpServletResponse.setContentType(<span class="hljs-string">&quot;application/json;charset=utf-8&quot;</span>);<br>        httpServletResponse.setStatus(status);<br>        httpServletResponse.getWriter().println(<span class="hljs-string">&quot;&#123;\&quot;message\&quot;: \&quot;&quot;</span> + msg + <span class="hljs-string">&quot;\&quot;， \&quot;status\&quot;: &quot;</span> + status + <span class="hljs-string">&quot;&#125;&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>重启服务后新建授权规则</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410221758019.png" srcset="/img/loading.gif" lazyload alt="image-20240410221758019"></p>
<blockquote>
<p>也可以使用<code>@SentinelResource</code>注解方式</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;order&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrdersController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> OrdersService ordersService;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;orderFindOne&quot;,blockHandlerClass = GlobeBlockException.class,blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> Orders <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-keyword">return</span> ordersService.queryByOrderId(id);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobeBlockException</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Orders <span class="hljs-title function_">blockHandler</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id, BlockException e)</span> &#123;<br>        log.error(<span class="hljs-string">&quot;当前服务繁忙，请稍后再试&quot;</span>);<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Orders</span>();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>配置授权规则</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410222328041.png" srcset="/img/loading.gif" lazyload alt="image-20240410222328041"></p>
<blockquote>
<p>虽然只能返回与使用<code>@SentinelResource</code>注解标注的方法<code>相同</code>的<code>返回值类型</code>，但是在<code>springboot</code>的<code>restful</code>风格的api中，我们通常会统一返回结果<br>如<code>JsonData</code>或者<code>R</code>等统一返回值类型</p>
</blockquote>
<h2 id="规则持久化"><a href="#规则持久化" class="headerlink" title="规则持久化"></a>规则持久化</h2><h3 id="概念-2"><a href="#概念-2" class="headerlink" title="概念"></a>概念</h3><p>Sentinel的控制台规则管理有三种模式:</p>
<ol>
<li>原始模式: Sentinel的默认模式，将规则保存在内存,重启服务会丢失。</li>
<li>pull模式</li>
<li>push模式</li>
</ol>
<h4 id="pull模式"><a href="#pull模式" class="headerlink" title="pull模式"></a>pull模式</h4><p>pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询，更新本地规则。</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081715561.png" srcset="/img/loading.gif" lazyload alt="image-20240409081715561"></p>
<blockquote>
<p>由于是定时轮询读取，所以存在<code>时效性</code>问题，导致了服务中<code>规则</code>不一致的问题</p>
</blockquote>
<h4 id="push模式"><a href="#push模式" class="headerlink" title="push模式"></a>push模式</h4><p>push模式:控制台将配置规则推送到远程配置中心，例如Nacos。Sentinel客户 端监听Nacos,获取配置变更的推送消息，完成本地配置更新。</p>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240409081844429.png" srcset="/img/loading.gif" lazyload alt="image-20240409081844429"></p>
<h4 id="总结-2"><a href="#总结-2" class="headerlink" title="总结"></a>总结</h4><p>Sentinel的三种配置管理模式是什么?</p>
<ol>
<li>原始模式:保存在内存</li>
<li>pull模式:保存在本地文件或数据库，定时去读取</li>
<li>push模式:保存在nacos，监听变更实时更新</li>
</ol>
<h3 id="实现push模式"><a href="#实现push模式" class="headerlink" title="实现push模式"></a>实现push模式</h3><blockquote>
<p>参考<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_36763419/article/details/121560105">博客</a></p>
</blockquote>
<p>push模式实现最为复杂，依赖于nacos,并且需要修改Sentinel控制台源码。</p>
<p><strong>引入依赖</strong></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.csp<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>sentinel-datasource-nacos<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-alibaba-sentinel-datasource<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p><strong>配置nacos地址</strong></p>
<blockquote>
<p>示例如下</p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br>    <span class="hljs-attr">cloud:</span><br>        <span class="hljs-attr">sentinel:</span><br>            <span class="hljs-attr">datasource:</span><br>                <span class="hljs-attr">flow:</span><br>                    <span class="hljs-attr">nacos:</span><br>                    	<span class="hljs-comment"># nacos地址</span><br>                        <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span> <br>                        <span class="hljs-attr">dataId:</span> <span class="hljs-string">orderservice-flow-rules</span><br>                        <span class="hljs-attr">groupId:</span> <span class="hljs-string">SENTINEL_GROUP</span><br>                        <span class="hljs-comment">#还可以是: degrade(降级)、authority(授权)、 param-flow(参数限流)</span><br>                        <span class="hljs-attr">rule-type:</span> <span class="hljs-string">flow</span> <br>				<span class="hljs-comment"># 如果需要多个配置，继续添加即可，如下所示</span><br>                <span class="hljs-attr">degrade:</span><br>                    <span class="hljs-attr">nacos:</span><br>                        <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span> <br>                        <span class="hljs-attr">dataId:</span> <span class="hljs-string">orderservice-degrade-rules</span><br>                        <span class="hljs-attr">groupId:</span> <span class="hljs-string">SENTINEL_GROUP</span><br>                        <span class="hljs-attr">rule-type:</span> <span class="hljs-string">degrade</span> <br></code></pre></td></tr></table></figure>

<blockquote>
<p>以上是配置格式，接下来正式配置<code>user</code></p>
</blockquote>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">server:</span><br>  <span class="hljs-attr">port:</span> <span class="hljs-number">8080</span><br><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">user-service</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">nacos:</span><br>      <span class="hljs-attr">config:</span><br>        <span class="hljs-attr">file-extension:</span> <span class="hljs-string">properties</span><br>        <span class="hljs-attr">namespace:</span> <span class="hljs-string">d2455f6d-ed00-41ba-9915-09021bc0df6c</span><br>        <span class="hljs-attr">prefix:</span> <span class="hljs-string">$&#123;spring.application.name&#125;</span><br>        <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br>      <span class="hljs-attr">discovery:</span><br>        <span class="hljs-attr">cluster-name:</span> <span class="hljs-string">JS</span><br>        <span class="hljs-attr">ephemeral:</span> <span class="hljs-literal">false</span><br>        <span class="hljs-attr">namespace:</span> <span class="hljs-string">d2455f6d-ed00-41ba-9915-09021bc0df6c</span><br>        <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br>    <span class="hljs-attr">sentinel:</span><br>      <span class="hljs-attr">transport:</span><br>        <span class="hljs-attr">dashboard:</span> <span class="hljs-string">localhost:8089</span><br>      <span class="hljs-attr">web-context-unify:</span> <span class="hljs-literal">false</span><br>      <span class="hljs-attr">datasource:</span><br>        <span class="hljs-attr">flow:</span><br>          <span class="hljs-attr">nacos:</span><br>            <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br>            <span class="hljs-attr">namespace:</span> <span class="hljs-string">d2455f6d-ed00-41ba-9915-09021bc0df6c</span><br>            <span class="hljs-attr">data-id:</span> <span class="hljs-string">user-service-sentinel</span><br>            <span class="hljs-attr">group-id:</span> <span class="hljs-string">DEFAULT_GROUP</span><br>            <span class="hljs-attr">data-type:</span> <span class="hljs-string">json</span><br>            <span class="hljs-comment">#还可以是: degrade(降级)、authority(授权)、 param-flow(参数限流)</span><br>            <span class="hljs-attr">rule-type:</span> <span class="hljs-string">flow</span><br>  <span class="hljs-attr">config:</span><br>    <span class="hljs-attr">import:</span> <span class="hljs-string">nacos:$&#123;spring.cloud.nacos.config.prefix&#125;-$&#123;spring.profiles.active&#125;.$&#123;spring.cloud.nacos.config.file-extension&#125;?refresh=true</span><br>  <span class="hljs-attr">profiles:</span><br>    <span class="hljs-attr">active:</span> <span class="hljs-string">dev</span><br></code></pre></td></tr></table></figure>

<blockquote>
<p>上面配置中，sentinel规则配置到nacos dataId为<code>user-service-sentinel</code>的配置中了</p>
</blockquote>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">[</span><br>  <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;resource&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;userQueryById&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;controlBehavior&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;count&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;grade&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;limitApp&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;default&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;strategy&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">]</span><br></code></pre></td></tr></table></figure>

<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214414971.png" srcset="/img/loading.gif" lazyload alt="image-20240410214414971"></p>
<blockquote>
<p>以上配置是指</p>
<p>resource：资源名。<br>limitApp：来源应用。<br>grade：阈值类型。0 表示线程数，1 表示是QPS。<br>count：单机阈值。<br>strategy：流控模式。0 表示直接，1 表示关联，2 表示链路。<br>controlBehavior：流控效果。0 表示快速失败，1 表示Warm up，2 表示排队等待。<br>clusterMode：是否集群。false 表示否，true 表示是。</p>
</blockquote>
<p>**这里选择<code>resource</code>为<code>userQueryById</code>**，因为<code>@SentinelResource</code>标记资源为<code>userQueryById</code>还记得吗？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(&quot;user&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> &#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserService userService;<br><br>    <span class="hljs-meta">@SentinelResource(value = &quot;userQueryById&quot;,blockHandlerClass = GlobeBlockException.class,blockHandler = &quot;blockHandler&quot;)</span><br>    <span class="hljs-meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span><br>    <span class="hljs-keyword">public</span> User <span class="hljs-title function_">findOne</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id)</span> &#123;<br>        <span class="hljs-keyword">if</span>(id==<span class="hljs-number">2</span>)&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;抛出异常了&quot;</span>);<br>        &#125;<br>        <span class="hljs-keyword">return</span> userService.getById(id);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>测试一下是否可行</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214733973.png" srcset="/img/loading.gif" lazyload alt="image-20240410214733973"></p>
<blockquote>
<p>order也没问题</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214813076.png" srcset="/img/loading.gif" lazyload alt="image-20240410214813076"></p>
<blockquote>
<p>重启服务，查看<code>sentinel</code>控制台，规则是否还存在</p>
</blockquote>
<p><img src="https://gitee.com/tongstyle/blog-img/raw/master/img/image-20240410214914582.png" srcset="/img/loading.gif" lazyload alt="image-20240410214914582"></p>
<blockquote>
<p>但是有个需要注意的点，这是<code>sentinel</code>读<code>nacos</code>配置的规则，而在<code>sentinel</code>中配置的规则，不能同步到<code>nacos</code></p>
<p>如果需要让<code>sentinel</code>的配置同步到<code>nacos</code>，<code>nacos</code>修改的配置<code>sentinel</code>也能获取到，则需要修改<code>sentinel-dashboard.jar</code>的源码了</p>
<p>网上也有教程</p>
</blockquote>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/Sentinel/" class="category-chain-item">Sentinel</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/Sentinel/">#Sentinel</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>Sentinel</div>
      <div>http://example.com/2024/04/10/编程语言/Java/学习/框架/Sentinel/Sentinel/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>深海火锅店</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2024年4月10日</div>
        </div>
      
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2024/04/10/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/Java/%E5%AD%A6%E4%B9%A0/%E6%A1%86%E6%9E%B6/SpringCloud/SpringCloud/" title="SpringCloud">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">SpringCloud</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2024/04/03/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/Java/%E5%AD%A6%E4%B9%A0/%E6%A1%86%E6%9E%B6/RabbitMq/RabbitMq/" title="RabbitMQ">
                        <span class="hidden-mobile">RabbitMQ</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>




  
<script src="//cdn.jsdelivr.net/gh/EmoryHuang/BlogBeautify@1.1/DynamicRibbon.min.js"></script>



<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
